package Derivation;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;

import Basics.Atom;
import Basics.DB;
import Basics.Proton;
import Basics.Rule;
import Basics.Var;

public class DeriveByRuleTopDown extends DeriveByRule
{
	
	
	
	public DeriveByRuleTopDown (Rule ir)
	{
		super(ir);
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: FindDerivationsForRule																				
	/** Description: Finds all Derivations for the rule according to db 				
	/*************************************************************************************************************/
	
	public void FindDerivationsForRuleTopDown () 
	{
		List<Rule> instRuleVector = new CopyOnWriteArrayList<Rule>();
		instRuleVector.add(new Rule(this.r));
		
		while (false == IsFullyInstList(instRuleVector))
		{			
			instRuleVector = InstIterationTopDown(instRuleVector);
		}
		
		for (Rule rule : instRuleVector) 
		{
			this.derivations.add(rule);
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: FindDerivationsForRule																				
	/** Description: Finds all Derivations for the rule according to vhildren's db For Intersection				
	/*************************************************************************************************************/
	
	public void FindDerivationsForRuleTopDownForIntersection (Map<String, Vector<Atom>> childrenDB) 
	{
		List<Rule> instRuleVector = new CopyOnWriteArrayList<Rule>();
		instRuleVector.add(new Rule(this.r));
		
		while (false == IsFullyInstList(instRuleVector))
		{			
			instRuleVector = InstIterationTopDownForIntersection(instRuleVector, childrenDB);
		}
		
		for (Rule rule : instRuleVector) 
		{
			this.derivations.add(rule);
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: InstIterationTopDown																				
	/** Description: One iteration to update the instRuleVector in partly inst rules  				
	/*************************************************************************************************************/
	
	public List<Rule> InstIterationTopDown (List<Rule> instRuleVector)
	{
		Vector<Proton> optionsForProton = null;
		List<Rule> toBeRemoved = new ArrayList<Rule>();
		for (Rule rule : instRuleVector)
		{
			Vector<Proton> varsInRule = GetListOfVarsInRule(rule);
			for (Proton var : varsInRule) 
			{
				optionsForProton = DB.getInstance().GetAllConstantsInCategory(var);
				if (null != optionsForProton) 
				{
					for (Proton c : optionsForProton) 
					{
						Rule copy = new Rule(rule);
						copy.SwapVarInRule(var, c);
						instRuleVector.add(copy);
					}

					toBeRemoved.add(rule);
				}
			}
		}
		
		instRuleVector.removeAll(toBeRemoved);
		return instRuleVector;
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: InstIterationTopDownForIntersection																				
	/** Description: One iteration to update the instRuleVector in partly inst rules For Intersection 				
	/*************************************************************************************************************/
	
	public List<Rule> InstIterationTopDownForIntersection (List<Rule> instRuleVector, Map<String, Vector<Atom>> childrenDB)
	{
		Vector<Proton> optionsForProton = null;
		List<Rule> toBeRemoved = new ArrayList<Rule>();
		for (Rule rule : instRuleVector)
		{
			Vector<Proton> varsInRule = GetListOfVarsInRule(rule);
			for (Proton var : varsInRule) 
			{
				optionsForProton = GetAllConstantsInCategory(var, childrenDB);
				if (null != optionsForProton) 
				{
					for (Proton c : optionsForProton) 
					{
						Rule copy = new Rule(rule);
						copy.SwapVarInRule(var, c);
						instRuleVector.add(copy);
					}

					toBeRemoved.add(rule);
				}
			}
		}
		
		instRuleVector.removeAll(toBeRemoved);
		return instRuleVector;
	}
	
	
	
	
	/*************************************************************************************************************/
	/** Title: GetListOfVarsInRule																				
	/** Description: Gets a List Of Vars In a Rule  				
	/*************************************************************************************************************/
	
	public Vector<Proton> GetListOfVarsInRule (Rule rule)
	{
		Vector<Proton> varsInRule = new Vector<Proton> ();
		for (Atom atom : rule.getBody()) 
		{
			if (false == atom.isFullyInst()) 
			{
				for (Proton p : atom.getParams()) 
				{
					if (p instanceof Var && false == varsInRule.contains(p)) 
					{
						varsInRule.add(p);
					}
				}
			}
		}
		
		return varsInRule;
	}
	
	
	/*************************************************************************************************************/
	/** Title: IsFullyInstList																				
	/** Description: Check if the list of rules contains only fully inst rules  				
	/*************************************************************************************************************/
	
	public boolean IsFullyInstList (List<Rule> instRuleVector)
	{
		boolean fullyInstList = true;
		for (Rule rule : instRuleVector) 
		{
			if (false == rule.isFullyInst()) 
			{
				fullyInstList = false;
			}
		}
		
		return fullyInstList;
	}
	
	
	
	/*************************************************************************************************************/
    /** Title: GetAllConstantsInCategory                                                                                                                                                            
    /** Description: Gets all the atoms in the DB by proton category                        
    /*************************************************************************************************************/
    
    public Vector<Proton> GetAllConstantsInCategory (Proton p, Map<String, Vector<Atom>> childrenDB)
    {
            Vector<Proton> relevants = new Vector<Proton>();
            for (String key : childrenDB.keySet()) 
            {
                    for (Atom atom : childrenDB.get(key)) 
                    {
                            for (Proton proton : atom.getParams()) 
                            {
                                    if (true == proton.getCategory().equals(p.getCategory()) && false == relevants.contains(proton)) 
                                    {
                                            relevants.add(proton);
                                    }
                            }
                    }
            }
            
            return relevants;
    }
}
